package top.tenebrous.yzl.im.config;

import com.alibaba.fastjson.JSON;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.core.Ordered;
import org.springframework.core.io.ClassPathResource;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.util.CollectionUtils;
import top.tenebrous.yzl.im.analysis.Finder;
import top.tenebrous.yzl.im.annotation.AutoConfigChatRecord;
import top.tenebrous.yzl.im.annotation.ImProducer;
import top.tenebrous.yzl.im.client.WebSocketHandlerClient;
import top.tenebrous.yzl.im.context.ImContext;
import top.tenebrous.yzl.im.entity.ImWebSocketProperties;
import top.tenebrous.yzl.im.enums.ProducerType;
import top.tenebrous.yzl.im.factory.colony.MessageQueueFactory;
import top.tenebrous.yzl.im.interceptor.ImWebsocketInterceptor;
import top.tenebrous.yzl.im.observer.AbstractChatRecord;
import top.tenebrous.yzl.im.observer.ImChatRecordHandlerProcessorObserver;
import top.tenebrous.yzl.im.observer.ObserverInfoBean;
import top.tenebrous.yzl.im.sender.AbstractSenderHandler;
import top.tenebrous.yzl.im.utils.WebSocketUtils;

import javax.annotation.Resource;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * @Description:
 * @Project: jestic-spring-boot
 * @Package: com.zy.tenebrous.jestic.config
 * @Author: zhangya
 * @CreateTime: 2023-01-19  10:33
 * @Version: 1.0.0
 */
public class EnableLoadImConfiguration implements InitializingBean, Ordered {

    private static final Logger logger = LoggerFactory.getLogger(EnableLoadImConfiguration.class);

    @Qualifier("yzlImScheduledThreadPoolScheduler")
    @Autowired
    private ScheduledExecutorService scheduledExecutorService;

    @Qualifier("yzlImThreadPoolScheduler")
    @Autowired
    private ThreadPoolTaskExecutor taskExecutor;

    @Resource
    private WebSocketHandlerClient webSocketHandlerClient;

    @Autowired
    private ApplicationContext applicationContext;

    @Resource
    private AbstractChatRecord chatRecord;

    @Resource
    private ImContext imContext;

    @Resource
    private MessageQueueFactory messageQueueFactory;

    @Value("${server.servlet.context-path:/}")
    private String contextPath;

    private final ImWebSocketProperties webSocketProperties;

    private final List<String> webSocketList = new ArrayList<>();

    public EnableLoadImConfiguration(ImWebSocketProperties webSocketProperties) {
        this.webSocketProperties = webSocketProperties;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        for (ImWebSocketProperties.ServerInfos serverInfo : webSocketProperties.getServerInfo()) {
            webSocketList.add(webSocketProperties.getPath(contextPath, serverInfo));
        }
        logger.info("启动客户端连接服务端开始");
        TimerTask run = new TimerTask() {
            @Override
            public void run() {
                // 获取客户端地址
                if (!CollectionUtils.isEmpty(webSocketList)) {
                    for (String webSocket : webSocketList) {
                        if (!WebSocketUtils.getConnectedSession().contains(webSocket)) {
                            logger.info("客户端--->监听到新节点发布，准备连接:{}", webSocket);
                            webSocketHandlerClient.connectPeer(webSocket);
                            WebSocketUtils.addConnectedSession(webSocket);
                        }
                    }
                }
            }
        };
        scheduledExecutorService.scheduleWithFixedDelay(run, 8, 5, TimeUnit.SECONDS);

        // 检查客户端是否全部连上服务端
        taskExecutor.execute(()->{
            while (true) {
                List<String> connectedSession = WebSocketUtils.getConnectedSession();
                if (connectedSession.size() == webSocketList.size()) {
                    // 停止定时线程
                    scheduledExecutorService.shutdownNow();
                    logger.info("客户端 ---> 当前客户端已完成所有服务端连接:\n{}", JSON.toJSONString(connectedSession, true));
                    break;
                }
            }
        });

        // 将聊天记录观察者 加入观察者池
        taskExecutor.execute(() -> {
            Map<String, ImChatRecordHandlerProcessorObserver> beans = applicationContext.getBeansOfType(ImChatRecordHandlerProcessorObserver.class);
            for (Map.Entry<String, ImChatRecordHandlerProcessorObserver> observerEntry : beans.entrySet()) {
                ImChatRecordHandlerProcessorObserver value = observerEntry.getValue();

                // 解析观察者
                AutoConfigChatRecord annotation = value.getClass().getAnnotation(AutoConfigChatRecord.class);
                Map<ImChatRecordHandlerProcessorObserver, ObserverInfoBean> map = new HashMap<>();

                ObserverInfoBean observerInfoBean = new ObserverInfoBean();
                observerInfoBean.setEnableAsync(Objects.isNull(annotation) ? true : annotation.enableAsync());
                observerInfoBean.setThreadPoolName(Objects.isNull(annotation) ? "jesticAccountThreadPoolScheduler" : annotation.threadPool());
                observerInfoBean.setThreadPoolClass(Objects.isNull(annotation) ? ThreadPoolTaskExecutor.class : annotation.threadPoolClass());
                observerInfoBean.setEnableLocalStorage(Objects.isNull(annotation) ? false : annotation.enableLocalStorage());
                observerInfoBean.setLocalStoragePath(Objects.isNull(annotation) ? "" : annotation.localStoragePath());

                map.put(observerEntry.getValue(), observerInfoBean);
                chatRecord.addObserver(observerEntry.getValue());
                chatRecord.addObserver(map);
            }
        });

        taskExecutor.execute(() -> {
            Map<String, ImWebsocketInterceptor> interceptorMap = applicationContext.getBeansOfType(ImWebsocketInterceptor.class);
            for (Map.Entry<String, ImWebsocketInterceptor> entry : interceptorMap.entrySet()) {
                // 将实现存入池内
                imContext.setInterceptorMap(entry.getKey(), entry.getValue());
                logger.info("yzl-im:new websocket interceptor [{}] add pool", entry.getKey());
            }
            logger.info("yzl-im:current interceptor pool have [{}] extend", interceptorMap.size());

            Map<String, AbstractSenderHandler> senderHandlerMap = applicationContext.getBeansOfType(AbstractSenderHandler.class);
            for (Map.Entry<String, AbstractSenderHandler> entry : senderHandlerMap.entrySet()) {
                ProducerType type = ProducerType.CUSTOM;
                AbstractSenderHandler entryValue = entry.getValue();
                ImProducer imProducer = entryValue.getClass().getAnnotation(ImProducer.class);
                if (Objects.nonNull(imProducer)) {
                    type = imProducer.type();
                }
                messageQueueFactory.registerProducer(type, entryValue);
            }
            logger.info("yzl-im:注册自定义消息发送器完成;当前自定义注册器:[{}]", JSON.toJSONString(senderHandlerMap.keySet()));
        });

        // 加载敏感词
        taskExecutor.execute(() -> {
            List<String> strList = new ArrayList<>();
            ClassPathResource classPathResource = new ClassPathResource(webSocketProperties.getSensitiveWordName());
            try {
                InputStream in = classPathResource.getInputStream();

                InputStreamReader inputStreamReader = null;
                BufferedReader bufferedReader = null;
                try {
                    // 将字节流向字符流转换
                    inputStreamReader = new InputStreamReader(in, StandardCharsets.UTF_8);
                    // 创建字符流缓冲区
                    bufferedReader = new BufferedReader(inputStreamReader);
                    String line;
                    // 按行读取
                    while ((line = bufferedReader.readLine()) != null) {
                        strList.add(line);
                    }
                    Finder.addSensitiveWords(strList.toArray(new String[]{}));
                } catch (UnsupportedEncodingException e) {
                    logger.error(e.getMessage());
                } catch (FileNotFoundException e) {
                    logger.error(e.getMessage());
                } catch (IOException e) {
                    logger.error(e.getMessage());
                } finally {
                    if (inputStreamReader != null) {
                        try {
                            inputStreamReader.close();
                        } catch (IOException e) {
                            logger.error(e.getMessage());
                        }
                    }
                    if (bufferedReader != null) {
                        try {
                            bufferedReader.close();
                        } catch (IOException e) {
                            logger.error(e.getMessage());
                        }
                    }
                }

            } catch (IOException e) {
                logger.error(e.getMessage());
            }

        });
    }

    @Override
    public int getOrder() {
        return LOWEST_PRECEDENCE;
    }

}
